package com.shiku.imserver;

import com.shiku.imserver.cluster.ImClusterServiceIfc;
import com.shiku.imserver.common.message.AbstractMessage;
import com.shiku.imserver.common.message.ChatMessage;
import com.shiku.imserver.common.message.ErrorMessage;
import com.shiku.imserver.common.packets.ImPacket;
import com.shiku.imserver.common.utils.Callback;
import com.shiku.imserver.common.utils.ThreadUtil;
import com.shiku.imserver.message.MessageFactory;
import com.shiku.imserver.service.IMBeanUtils;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;


import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.cluster.TioClusterConfig;
import org.tio.core.ChannelContext;
import org.tio.core.GroupContext;
import org.tio.core.Tio;
import org.tio.core.intf.Packet;
import org.tio.utils.lock.SetWithLock;


public class CoreService {
  private static Logger log = LoggerFactory.getLogger(CoreService.class);
  
  public static String parseResource(String jid) {
    if (jid == null)
      return null; 
    int slashIndex = jid.indexOf('/');
    if (slashIndex + 1 > jid.length() || slashIndex < 0)
      return ""; 
    return jid.substring(slashIndex + 1);
  }
  
  public static String parseBareUserId(String jid) {
    int slashIndex = jid.indexOf('/');
    if (slashIndex < 0)
      return jid; 
    if (slashIndex == 0)
      return ""; 
    return jid.substring(0, slashIndex);
  }
  
  public static void bindUser(ChannelContext channelContext, String userid) {
    String baseId = "";
    channelContext.groupContext.users.bind(userid, channelContext);
  }
  
  public static boolean send(ChannelContext channelContext, ImPacket packet) {
    return Tio.send(channelContext, (Packet)packet).booleanValue();
  }
  
  public static boolean bSend(ChannelContext channelContext, ImPacket packet) {
    return Tio.bSend(channelContext, (Packet)packet).booleanValue();
  }
  
  public static Boolean sendToUser(GroupContext groupContext, String userid, ImPacket packet) {
    return sendToUser(groupContext, userid, packet, false);
  }
  
  private static Boolean sendToUser(GroupContext groupContext, String userid, ImPacket packet, boolean isBlock) {
    SetWithLock<ChannelContext> setWithLock = groupContext.users.find(groupContext, userid);
    try {
      if (setWithLock == null)
        return Boolean.valueOf(false); 
      ReentrantReadWriteLock.ReadLock readLock = setWithLock.readLock();
      readLock.lock();
      try {
        Set<ChannelContext> set = (Set<ChannelContext>)setWithLock.getObj();
        boolean ret = false;
        for (ChannelContext channelContext : set) {
          boolean singleRet = false;
          if (isBlock) {
            singleRet = bSend(channelContext, packet);
          } else {
            singleRet = send(channelContext, packet);
          } 
          if (singleRet)
            ret = true; 
        } 
        return Boolean.valueOf(ret);
      } catch (Throwable e) {
        log.error(e.getMessage(), e);
      } finally {
        readLock.unlock();
      } 
      return Boolean.valueOf(false);
    } finally {
      if (groupContext.isCluster() && !packet.isFromCluster()) {
        TioClusterConfig tioClusterConfig = groupContext.getTioClusterConfig();
        if (tioClusterConfig.isCluster4user())
          Tio.notifyClusterForUser(groupContext, userid, (Packet)packet); 
      } 
    } 
  }
  
  public static Boolean sendToUserOtherResource(GroupContext groupContext, String userid, String channelContextId, ImPacket packet, boolean isBlock) {
    SetWithLock<ChannelContext> setWithLock = groupContext.users.find(groupContext, userid);
    try {
      if (setWithLock == null)
        return Boolean.valueOf(false); 
      if (1 == setWithLock.size())
        return Boolean.valueOf(true); 
      ReentrantReadWriteLock.ReadLock readLock = setWithLock.readLock();
      readLock.lock();
      try {
        Set<ChannelContext> set = (Set<ChannelContext>)setWithLock.getObj();
        boolean ret = false;
        for (ChannelContext channelContext : set) {
          if (channelContext.getId().equals(channelContextId))
            continue; 
          boolean singleRet = false;
          if (isBlock) {
            singleRet = bSend(channelContext, packet);
          } else {
            singleRet = send(channelContext, packet);
          } 
          if (singleRet)
            ret = true; 
        } 
        return Boolean.valueOf(ret);
      } catch (Throwable e) {
        log.error(e.getMessage(), e);
      } finally {
        readLock.unlock();
      } 
      return Boolean.valueOf(false);
    } finally {
      if (groupContext.isCluster() && !packet.isFromCluster()) {
        TioClusterConfig tioClusterConfig = groupContext.getTioClusterConfig();
        if (tioClusterConfig.isCluster4user())
          Tio.notifyClusterForUser(groupContext, userid, (Packet)packet); 
      } 
    } 
  }
  
  public static Boolean sendToUserResource(GroupContext groupContext, String userid, String channelContextId, ImPacket packet, boolean isBlock) {
    SetWithLock<ChannelContext> setWithLock = groupContext.users.find(groupContext, userid);
    try {
      if (setWithLock == null)
        return Boolean.valueOf(false); 
      if (1 == setWithLock.size())
        return Boolean.valueOf(true); 
      ReentrantReadWriteLock.ReadLock readLock = setWithLock.readLock();
      readLock.lock();
      try {
        Set<ChannelContext> set = (Set<ChannelContext>)setWithLock.getObj();
        boolean ret = false;
        for (ChannelContext channelContext : set) {
          if (channelContext.getId().equals(channelContextId))
            continue; 
          boolean singleRet = false;
          if (isBlock) {
            singleRet = bSend(channelContext, packet);
          } else {
            singleRet = send(channelContext, packet);
          } 
          if (singleRet)
            ret = true; 
        } 
        return Boolean.valueOf(ret);
      } catch (Throwable e) {
        log.error(e.getMessage(), e);
      } finally {
        readLock.unlock();
      } 
      return Boolean.valueOf(false);
    } finally {
      if (groupContext.isCluster() && !packet.isFromCluster()) {
        TioClusterConfig tioClusterConfig = groupContext.getTioClusterConfig();
        if (tioClusterConfig.isCluster4user())
          Tio.notifyClusterForUser(groupContext, userid, (Packet)packet); 
      } 
    } 
  }
  
  public static Boolean sendToToken(GroupContext groupContext, String token, Packet packet) {
    return Tio.sendToToken(groupContext, token, packet);
  }
  
  public static ChannelContext getChannelContextByUserIdResource(GroupContext groupContext, String userId, String userResource) {
    SetWithLock<ChannelContext> setWithLock = Tio.getChannelContextsByUserid(groupContext, userId);
    if (null == setWithLock)
      return null; 
    try {
      ReentrantReadWriteLock.ReadLock readLock = setWithLock.readLock();
      readLock.lock();
      try {
        Set<ChannelContext> set = (Set<ChannelContext>)setWithLock.getObj();
        for (ChannelContext channelContext : set) {
          Object resource = channelContext.getAttribute("resource");
          if (null != resource && resource.equals(userResource))
            return channelContext; 
        } 
        return null;
      } catch (Throwable e) {
        log.error(e.getMessage(), e);
        return null;
      } finally {
        readLock.unlock();
      } 
    } catch (Exception e) {
      log.error(e.getMessage(), e);
      return null;
    } 
  }
  
  public static Set<String> getChannelContextResources(GroupContext groupContext, String userId) {
    Set<String> resources = new HashSet<>();
    SetWithLock<ChannelContext> setWithLock = Tio.getChannelContextsByUserid(groupContext, userId);
    if (null == setWithLock)
      return null; 
    try {
      ReentrantReadWriteLock.ReadLock readLock = setWithLock.readLock();
      readLock.lock();
      try {
        Set<ChannelContext> set = (Set<ChannelContext>)setWithLock.getObj();
        for (ChannelContext channelContext : set) {
          Object resource = channelContext.getAttribute("resource");
          resources.add(resource.toString());
        } 
        return resources;
      } catch (Throwable e) {
        log.error(e.getMessage(), e);
        return resources;
      } finally {
        readLock.unlock();
      } 
    } catch (Exception e) {
      log.error(e.getMessage(), e);
      return resources;
    } 
  }
  
  public static void userOnline(final String connStr, final String userId, final ChannelContext channelContext) {
    log.info("[ww]==111=userOnline="+connStr+",userId="+userId);
    ThreadUtil.executeInThread(new Callback() {
          public void execute(Object obj) {
            IMBeanUtils.getRocketmqService().handleLogin(connStr);
            List<String> jidList = IMBeanUtils.getRedisService().queryUserRoomJidList(Integer.valueOf(userId));
            //监听用户所在群的消息
            for (String jid : jidList)
              Tio.bindGroup(channelContext, jid); 
            
            //发送开始收取个人聊天离线消息
            ImPacket packetBegin = MessageFactory.createReceivingBeginIMPacket(channelContext);
            CoreService.bSend(channelContext, packetBegin); 
            List<ChatMessage> queue = IMBeanUtils.getMessageRepository().loadChatOffToJID(userId, true);
            ImPacket packet = null;
            for (ChatMessage chatMessage : queue){
              //先删除redis里面存在，避免消息重发
              IMBeanUtils.getRedisService().removeWillMessage(Integer.valueOf(userId),chatMessage.getMessageHead().getMessageId());
            }
            for (ChatMessage chatMessage : queue) {
              chatMessage.getMessageHead().setOffline(true);
              packet = MessageFactory.convertToImPacket((AbstractMessage)chatMessage);
              packet.setMessage(chatMessage);
              if (null != packet)
              {
                  boolean sendResult =  CoreService.bSend(channelContext, packet);
            	  if(sendResult)
            	  {
            	    //log.info("[ww]===sendMsgID="+chatMessage.getToUserId());
            		  IMBeanUtils.getMessageRepository().deleteChatOffToMessageID(chatMessage.getMessageHead().getMessageId(),chatMessage.getToUserId());
            	  }
                }
            }
            log.info("[ww]=22==userOnline="+connStr+",userId="+userId);
            //发送结束收取个人聊天离线消息
            ImPacket packetEnd = MessageFactory.createReceivingEndIMPacket(channelContext);
            CoreService.bSend(channelContext, packetEnd); 
          }
        });
  }
  
  public static void notifyUserConflict(ChannelContext channelContext, String connStr, String arg) {
    ErrorMessage loginConflict = new ErrorMessage();
    loginConflict.setMessageHead(MessageFactory.createMessageHead(channelContext));
    loginConflict.getMessageHead().setTo(connStr);
    loginConflict.setCode((short)-2);
    loginConflict.setArg(arg);
    ImPacket conflict = MessageFactory.createLoginConflict(loginConflict);
    Tio.send(channelContext, (Packet)conflict);
  }
  
  public static boolean userIsOnline(GroupContext groupContext, String userId) {
    SetWithLock<ChannelContext> setWithLock = Tio.getChannelContextsByUserid(groupContext, userId);
    ImClusterServiceIfc clusterService = IMBeanUtils.getBeanManager().getImClusterService();
    return ((null != setWithLock && 0 < setWithLock.size()) || (null != clusterService && clusterService
      .userIsOnline(userId)));
  }
}
